巨大なレイヤーを紐付けたLambda Functionの挙動を観察してみた
はじめに
サーバーレス開発部@大阪の岩田です。
2019年1月現在AWS Lambdaの制限として解凍後のデプロイパッケージサイズはレイヤーを含めて250Mまでという制限があります。 本エントリではサイズの大きなレイヤーを使い、デプロイパッケージのサイズを上限ギリギリまで大きくしていった際にどの様な影響があるかを観察してみます。
やること
サイズが49Mのレイヤーを5つ作成し、LambdaFunctionにレイヤーを1つ紐付けた場合、2つ紐付けた場合、3つ・・・と各パターンで
- AWS CLIでLambda Functionをデプロイするのにかかった時間の計測
- X-Rayを使ったLambda Function実行時間の計測
を行います。
事前準備
まず下記のシェルスクリプトを実行し、49Mのレイヤーを5つ作成します。
#!/bin/bash for i in `seq 1 5`; do dd if=/dev/urandom of=bigfile${i}.img count=49 bs=1m zip bigfile${i}.zip bigfile${i}.img aws s3 cp bigfile${i}.zip s3://<適当なS3バケット> aws lambda publish-layer-version --content S3Bucket=<適当なS3バケット>,S3Key=bigfile${i}.zip --layer-name bigfilelayer${i} done
レイヤーの準備ができたのでLambda Functionを準備します。 Lambda Functionのコードです。コード自体には特に意味はありません。
import json def lambda_handler(event, context): return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
デプロイ用にZIPに圧縮しておきます
zip handler.zip handler.py
計測してみる
ここからLambda Functionをデプロイしながらパターンごとに計測していきます。
レイヤー無しの場合
まずLambda Functionをデプロイします。 先ほど作成したZIPを指定しつつ、X-Rayを有効化しています。
time aws lambda create-function --runtime python3.6 --handler handler.lambda_handler --role arn:aws:iam::xxxxxxxxxxxx:role/lambda_basic_execution --function-name biglayer_function --zip-file fileb://handler.zip --tracing-config Mode=Active
デプロイの所要時間です。
real 0m1.426s user 0m0.522s sys 0m0.197s
サクッと終了しました。 適当なテストイベントを設定して実行、X-Rayのトレースを確認してみます。
initializationの表示が出ており、コールドスタートしていることが分かります。 コールドスタートしているにも関わらずトータルの所要時間は199msなので、かなり高速にサンドボックス環境を作成できていると言えるのではないでしょうか?
レイヤー1つの場合
次にレイヤーを1つ紐付けてデプロイパッケージのサイズを大きくしてみます。
time aws lambda update-function-configuration --function-name biglayer_function --layers arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:layer:bigfilelayer1:1
デプロイの所要時間です。
real 0m8.659s user 0m0.467s sys 0m0.136s
一気に遅くなったことが分かります。
X-Rayのトレース結果です。
Lambda Functionの所要時間は相変わらず数msレベルですが、総所要時間は752msとなっています。 この調子でレイヤーを増やしていき、レイヤー5つを紐付けた場合まで計測していきます。
結果と考察
最終的な結果は下記の通りです。 複数回計測すべきですが、計測結果が明らかに遅くなっていったので1回で切り上げました。
レイヤ数 | Lambda Function作成の所要時間 | Lambda Function実行の所要時間 |
---|---|---|
0 | 1.426s | 199ms |
1 | 8.659s | 752ms |
2 | 22.722s | 1.2s |
3 | 30.136s | 1.6s |
4 | 39.231s | 4.5s |
5 | 47.709s | 5.4s |
紐付けるレイヤーを増やしていくとLambda Function保存時の所要時間、Lambda Function実行の所要時間共に増えていきました。 レイヤー無しとレイヤー5つの場合を比較するとLambda Function保存時の所要時間は46秒程度、Lambda Function実行の所要時間は5秒程度遅くなっています。
勝手な妄想ですがDockerで例えるならLambda Function保存時はdocker build
相当の処理が、コールドスタート時にはdocker pull
相当の処理が裏で動いているのかもしれません。
Lambda Function保存時にdocker build
相当の処理が動いているのであれば、Lambda Layersの指定にLatestが使えないのも納得です。
まとめ
簡単にですが巨大なレイヤーを紐付けたLambda Functionの挙動を観察してみました。 また時間ができたらレイヤー無しの巨大なLambda Functionや、巨大なレイヤーを1つだけ紐づけたLambda Functionの挙動も確認してみたいと思います。 ※一度249Mのレイヤーを作ろうと試したのですが、タイムアウトしてレイヤー作成に失敗してしまいました。。
今年のre:Inventでこのあたりの内部構造の話が聞けることを期待したいです!